#include "arbre.hpp"

void initArbre(arbre &a, bool verbose)
{
    a.racine = NULL;
}

void initArbre(arbre &a, int entier, bool verbose)
{
    noeud* n = (noeud*) malloc(sizeof(noeud));

    if (verbose)
        cout << "entrée dans l'initialisation de l'arbre" << endl;
    initValeur(*n, entier, verbose);
    a.racine = n;
}

void initArbre(arbre &a, const arbre &b, bool verbose)
{

    initArbre(a, verbose);
    if(!arbreVide(b))
    {
        noeud* n = (noeud*) malloc(sizeof(noeud));
        initNoeud(*n, *(racine(b)), verbose);
        a.racine = n;
    }
}

void initArbre(arbre &a, const noeud &n, bool verbose)
{
    if (verbose)
        cout << "entrée dans initialisation d'arbre par copie de noeud" << endl;
    initArbre(a, verbose);
    noeud *racine;
    racine = (noeud*) malloc(sizeof(noeud));
    initNoeud(*racine, n, verbose);
    a.racine = racine;
    if (verbose)
        cout << "\tnoeud copié" << endl;
}

void initArbre(arbre &a, string expression, bool verbose)
{
    if (verbose)
        cout << "entrée dans l'initialisation d'arbre par expression" << endl;
    initArbre(a, verbose);
    if (verbose)
        cout << "arbre initialisé" << endl;

    if (!expression.empty())
    {
        expression = enleveChar(expression, ' ');
        if(verbose)
            cout << "l'expression transformée est : " << expression << endl;

        noeud* n = (noeud*) malloc(sizeof(noeud));
        initNoeud(*n, expression, verbose);
        a.racine = n;
    } else {
        if (verbose)
            cout << "l'expression est vide" << endl;
    }
}

void addArbre(const arbre &a, const arbre &b, arbre &resultat, bool verbose, bool affComplet)
{
    if (verbose)
        cout << endl << "entrée dans addArbre" << endl;

    if (arbreVide(a) || arbreVide(b))
    {
        if(arbreVide(a) && !arbreVide(b))
        {
            testamentArbre(resultat);
            initArbre(resultat, b, verbose);
        }
        else if(!arbreVide(a) && arbreVide(b))
        {
            testamentArbre(resultat);
            initArbre(resultat, a, verbose);
        }
        if (verbose)
            cout << "les deux arbres sont vides" << endl;
    }
    else
    {
        int nb_fils = 2;

        // on crée un noeud n qui sera la racine de resultat
        noeud* n = (noeud*) malloc(sizeof(noeud));
        initOperateur(*n, o_plus, nb_fils, verbose);

        // on ajoute la racine de a à n
        if((racine(a)->type == t_operateur) && (racine(a)->info.op == o_plus))
        { // si la racine de a est un plus, on ajoute ces fils à resultat
            for(unsigned int i =0; i<racine(a)->nbFils; i++)
            {
                noeud* temp = (noeud*) malloc(sizeof(noeud));
                initNoeud(*temp, *(racine(a)->fils[i]), verbose);
                ajoutFils(*n, temp, verbose);
            }
        }
        else
        {
            noeud* temp = (noeud*) malloc(sizeof(noeud));
            initNoeud(*temp, *racine(a), verbose);
            ajoutFils(*n, temp, verbose);
        }

        // on ajoute la racine de b à n
        if((racine(b)->type == t_operateur) && (racine(b)->info.op == o_plus))
        { // si la racine de b est un plus, on ajoute ces fils à resultat
            for(unsigned int i =0; i<racine(b)->nbFils; i++)
            {
                noeud* temp = (noeud*) malloc(sizeof(noeud));
                initNoeud(*temp, *(racine(b)->fils[i]), verbose);
                ajoutFils(*n, temp, verbose);
            }
        }
        else
        {
            noeud* temp = (noeud*) malloc(sizeof(noeud));
            initNoeud(*temp, *(racine(b)), verbose);
            ajoutFils(*n, temp, verbose);
        }

        if (affComplet)
        {
            affichageNoeudComplet(*n);
            for (unsigned int i=0; i < n->nbFils; i++)
                affichageNoeudComplet(*(n->fils[i]));
        }

        resultat.racine = n;
    }
}

void multArbre(const arbre &a, const arbre &b, arbre &resultat, bool verbose, bool affComplet)
{
    if (verbose)
        cout << endl << "entrée dans multArbre" << endl;


    if (arbreVide(a) || arbreVide(b))
    {
        if(arbreVide(a) && !arbreVide(b))
        {
            testamentArbre(resultat);
            initArbre(resultat, b, verbose);
        }
        else if(!arbreVide(a) && arbreVide(b))
        {
            testamentArbre(resultat);
            initArbre(resultat, a, verbose);
        }
        if (verbose)
            cout << "les deux arbres sont vides" << endl;
    }
    else
    {
        int nb_fils = 2;

        // on crée un noeud n qui sera la racine de resultat
        noeud* n = (noeud*) malloc(sizeof(noeud));
        initOperateur(*n, o_mult, nb_fils, verbose);

        // on ajoute la racine de a à n
        if((racine(a)->type == t_operateur) && (racine(a)->info.op == o_mult))
        { // si la racine de a est un mult, on ajoute ces fils à resultat
            for(unsigned int i =0; i<racine(a)->nbFils; i++)
            {
                noeud* temp = (noeud*) malloc(sizeof(noeud));
                initNoeud(*temp, *(racine(a)->fils[i]), verbose);
                ajoutFils(*n, temp, verbose);
            }
        }
        else
        {
            noeud* temp = (noeud*) malloc(sizeof(noeud));
            initNoeud(*temp, *racine(a), verbose);
            ajoutFils(*n, temp, verbose);
        }

        // on ajoute la racine de b à n
        if((racine(b)->type == t_operateur) && (racine(b)->info.op == o_mult))
        { // si la racine de b est un mult, on ajoute ces fils à resultat
            for(unsigned int i =0; i<racine(b)->nbFils; i++)
            {
                noeud* temp = (noeud*) malloc(sizeof(noeud));
                initNoeud(*temp, *(racine(b)->fils[i]), verbose);
                ajoutFils(*n, temp, verbose);
            }
        }
        else
        {
            noeud* temp = (noeud*) malloc(sizeof(noeud));
            initNoeud(*temp, *(racine(b)), verbose);
            ajoutFils(*n, temp, verbose);
        }

        if (affComplet)
        {
            affichageNoeudComplet(*n);
            for (unsigned int i=0; i < n->nbFils; i++)
                affichageNoeudComplet(*(n->fils[i]));
        }

        resultat.racine = n;
    }
}


int valeurArbre(const arbre &a)
{
    if(!arbreVide(a))
    {
        return(valeurNoeud(*racine(a)));
    } else {
        return 0;
    }
}

void affichageArbreExpression(const arbre& a)
{
    if(!arbreVide(a))
    {
        cout << "affichage de l'arbre en expression :" << endl;
        affichageNoeudExpression(*racine(a), true);
        cout << endl;
    } else {
        cout << "l'arbre est vide" << endl;
    }
}

void affichageArbreHorizontal(const arbre& a)
{
    if(!arbreVide(a))
    {
        cout << "affichage horizontal de l'arbre :" << endl;
        affichageNoeudHorizontal(*racine(a), 0);
        cout << endl;
    } else {
        cout << "l'arbre est vide" << endl;
    }
}

void developpeProduitArbre (const arbre &donne, arbre &dev, bool verbose)
{
    if (verbose)
        cout << "entrée dans le devellopement de produits d'arbres" << endl;
    if (arbreVide(donne))
    {
        cout << "l'arbre est vide" << endl;
    }
    else
    {
        if((racine(donne)->type == t_operateur) && (racine(donne)->info.op == o_mult) && (racine(donne)->nbFils == 2))
        {
            if (verbose)
                cout << "l'arbre représente une multiplication" << endl;

            noeud *pa, *pb;
            pa = racine(donne)->fils[0];
            pb = racine(donne)->fils[1];

            arbre a, b;
            if (verbose)
                cout << "initialisation du premier fils" << endl;
            initArbre(a, *pa, verbose);

            if (verbose)
                cout << "initialisation du deuxième fils" << endl;
            initArbre(b, *pb, verbose);

            if (verbose)
                cout << "les arbres a et b qui sont respectivement les deux fils de l'arbre donné sont initialisés" << endl;

            if((racine(a)->type == t_operateur && racine(a)->info.op == o_plus) && (racine(b)->type == t_operateur && racine(b)->info.op == o_plus))
            {
                if (verbose)
                    cout << "l'arbre peut etre developpé" << endl;

                arbre tmp_mult, tmp_add;

                for(unsigned int i = 0; i<racine(a)->nbFils; i++)
                {
                    arbre fils_a;
                    initArbre(fils_a, *(racine(a)->fils[i]), verbose);
                    for(unsigned int j = 0; j<racine(b)->nbFils; j++)
                    {
                        arbre fils_b;
                        initArbre(fils_b, *(racine(b)->fils[j]), verbose);
                        initArbre(tmp_add, verbose);
                        initArbre(tmp_mult, verbose);

                        multArbre(fils_a, fils_b, tmp_mult, verbose);
                        addArbre(dev, tmp_mult, tmp_add, verbose);

                        testamentArbre(dev, verbose);
                        initArbre(dev, tmp_add, verbose);

                        testamentArbre(tmp_add);
                        testamentArbre(tmp_mult);
                        testamentArbre(fils_b);
                    }
                    testamentArbre(fils_a);
                }

            } else {
                cout << "les fils de l'arbre ne sont pas des additions" << endl;
            }

            testamentArbre(a, verbose);
            testamentArbre(b, verbose);
        }
        else
        {
            cout << "l'arbre ne représente pas une multiplication de deux éléments" << endl;
        }
    }

}

noeud* racine(const arbre &a)
{
    return (a.racine);
}

bool arbreVide(const arbre &a)
{
    return (a.racine == NULL);
}

bool arbreFeuille(const arbre &a)
{
    return noeudFeuille(*racine(a));
}

void testamentArbre(arbre &a, bool verbose)
{
    if(!arbreVide(a))
    {
        if (verbose)
            cout << "l'arbre n'est pas vide" << endl;
        testamentNoeud(*(racine(a)), verbose);
        a.racine = NULL;
    }
}
